#include "net_task.h"

#include <arpa/inet.h>
#include <errno.h>
#include <netdb.h>  // struct addrinfo
#include <string.h>
#include <sys/socket.h>

#include "data_define.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_mac.h"
#include "esp_netif.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "nvs.h"
#include "nvs_flash.h"

const static char *TAG = "net_task";
char send_buf[NET_QUEUE_BUF_SIZE] = {0};
char recv_buf[NET_QUEUE_BUF_SIZE] = {0};

static net_state_t g_state = S_WIFI_INIT;
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
#define TCP_CONNECTED_BIT BIT2
#define TCP_FAIL_BIT BIT3
static EventGroupHandle_t s_event_group;
TaskHandle_t net_task_handle = NULL;
static int sock = -1;

static void event_handler(void *arg, esp_event_base_t event_base,
                          int32_t event_id, void *event_data) {
  if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
    esp_wifi_connect();
  } else if (event_base == WIFI_EVENT &&
             event_id == WIFI_EVENT_STA_DISCONNECTED) {
    ESP_LOGI(TAG, "Wi-Fi disconnected, retry...");
    xEventGroupClearBits(s_event_group, WIFI_CONNECTED_BIT);
    xEventGroupSetBits(s_event_group, WIFI_FAIL_BIT);
  } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
    ip_event_got_ip_t *e = (ip_event_got_ip_t *)event_data;
    ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&e->ip_info.ip));
    xEventGroupSetBits(s_event_group, WIFI_CONNECTED_BIT);
  }
}

static void wifi_init_sta(void) {
  s_event_group = xEventGroupCreate();
  ESP_ERROR_CHECK(esp_netif_init());
  ESP_ERROR_CHECK(esp_event_loop_create_default());
  esp_netif_create_default_wifi_sta();
  wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
  ESP_ERROR_CHECK(esp_wifi_init(&cfg));
  esp_event_handler_instance_t instance_any_id, instance_got_ip;
  ESP_ERROR_CHECK(esp_event_handler_instance_register(
      WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &instance_any_id));
  ESP_ERROR_CHECK(esp_event_handler_instance_register(
      IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &instance_got_ip));
  wifi_config_t wifi_config = {0};
  strcpy((char *)wifi_config.sta.ssid, g_cfg.wifi_ssid);
  strcpy((char *)wifi_config.sta.password, g_cfg.wifi_pass);
  wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
  ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
  ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
  ESP_ERROR_CHECK(esp_wifi_start());
}

static void tcp_disconnect(void) {
  if (sock >= 0) {
    shutdown(sock, SHUT_RDWR);
    close(sock);
  }
  sock = -1;
}

static bool tcp_connect(void) {
  tcp_disconnect();
  struct sockaddr_in dest_addr = {0};
  dest_addr.sin_addr.s_addr = inet_addr(g_cfg.tcp_server_ip);
  dest_addr.sin_family = AF_INET;
  dest_addr.sin_port = htons(g_cfg.tcp_server_port);
  sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
  if (sock < 0) return false;
  if (connect(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr)) != 0) {
    tcp_disconnect();
    return false;
  }
  int flag = fcntl(sock, F_GETFL, 0);
  if (flag == -1) {
    tcp_disconnect();
    return false;
  }
  if (fcntl(sock, F_SETFL, flag | O_NONBLOCK) == -1) {
    tcp_disconnect();
    return false;
  }

  return true;
}
#if 0
/// @brief 创建并连接到tcp服务器
/// @param ip_addr 服务器IP 字符串形式 例如:"192.168.1.100"
/// @param port 服务器监听的端口号
/// @param sock_fd 指向int的指针,用于传出创建的socket文件描述符
/// @return true 连接成功
/// @return false 连接失败
bool connect_to_tcp_server(const char *ip_addr, int port, int *sock_fd) {
  ESP_LOGI(TAG, "%s\n%d", ip_addr, port);
  if (ip_addr == NULL || sock_fd == NULL || port <= 0) {
    ESP_LOGE(TAG, "invalid parameter");
    return false;
  }
  *sock_fd = socket(AF_INET, SOCK_STREAM, 0);
  if (*sock_fd < 0) {
    ESP_LOGE(TAG, "socket create failed");
    perror("socket creation failed");
    return false;
  }
  struct sockaddr_in server_addr;
  memset(&server_addr, 0, sizeof(server_addr));
  server_addr.sin_family = AF_INET;
  server_addr.sin_port = htons(port);
  if (inet_pton(AF_INET, ip_addr, &server_addr.sin_addr) <= 0) {
    ESP_LOGE(TAG, "invalid address or address not supported: %s", ip_addr);
    close(*sock_fd);
    return false;
  }
  if (connect(*sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) <
      0) {
    ESP_LOGI("free_heap_size", "%ld", esp_get_free_heap_size());
    ESP_LOGE(TAG, "connection failed:%s", strerror(errno));
    close(*sock_fd);
    return false;
  }
  ESP_LOGI(TAG, "connect to server at %s:%d successfully", ip_addr, port);
  return true;
}

/**
 * @brief 发送数据到TCP连接
 *
 * @param sock 套接字
 * @param data 数据
 * @param length 数据长度
 * @return int 成功发送的字节数或错误码
 */
int send_bytes(int sock, const void *data, size_t length) {
  ssize_t sent_len = 0;
  int err;

  while (sent_len < length) {
    ssize_t ret =
        send(sock, (const char *)data + sent_len, length - sent_len, 0);
    if (ret == -1) {  // 发送失败

      net_work_status = net_task_got_ip;
      err = errno;  // 获取错误码
      switch (err) {
        case EIO:
          ESP_LOGE("SOCKET_SEND", "Network error occurred: %s", strerror(err));
          return 0;
        case ENOTCONN:
        case EPIPE:
          ESP_LOGE("SOCKET_SEND", "Socket disconnected: %s", strerror(err));
          return 0;
        default:
          ESP_LOGE("SOCKET_SEND", "Unknown error occurred: %s", strerror(err));
          return 0;
      }
    }
    sent_len += ret;
  }

  return sent_len;  // 返回成功发送的字节数
}
#endif
void wifi_task_entry(void *arg) {
  const int reconnect_delay_ms[] = {
      1000, 2000, 4000, 8000, 10000,
  };
  int retry_idx = 0;
  for (;;) {
    // ESP_LOGI("net_task", "state:%d", g_state);

    switch (g_state) {
      case S_WIFI_INIT: {
        wifi_init_sta();
        g_state = S_WIFI_CONNECTINHG;
      } break;
      case S_WIFI_CONNECTINHG: {
        xEventGroupClearBits(s_event_group, WIFI_FAIL_BIT);
        EventBits_t bits = xEventGroupWaitBits(
            s_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE,
            portMAX_DELAY);
        if (bits & WIFI_CONNECTED_BIT) {
          retry_idx = 0;
          g_state = S_WIFI_CONNECTED;
        } else {
          vTaskDelay(pdMS_TO_TICKS(reconnect_delay_ms[retry_idx]));
          retry_idx = (retry_idx + 1) % 5;
        }
      } break;
      case S_WIFI_CONNECTED: {
        g_state = S_TCP_CONNECTING;
      } break;
      case S_TCP_CONNECTING: {
        if (tcp_connect()) {
          retry_idx = 0;
          g_state = S_TCP_CONNECTED;
        }

      } break;
      case S_TCP_CONNECTED: {
        g_state = S_TCP_TX_RX;
      } break;
      case S_TCP_TX_RX: {
#include "queue_wrapper.h"
        if (queue_tx_pop(&send_buf[0], MAX_STR_LEN)) {
          send(sock, send_buf, strlen(send_buf), 0);
          ESP_LOGI("net_task", "tx:%s", send_buf);
        }
        int rx_len = recv(sock, recv_buf, sizeof(recv_buf) - 1, 0);
        if (rx_len > 0) { /* 正常接收数据 */
          recv_buf[rx_len] = 0;
          queue_rx_push(recv_buf);
        } else if (rx_len == 0) { /* 对方关闭了连接 */
          tcp_disconnect();
          g_state = S_TCP_CONNECTING;
        } else if (rx_len < 0) {
          if (errno == EWOULDBLOCK || errno == EAGAIN) {
            /* 无事可做 */
          } else {
            tcp_disconnect();
            g_state = S_TCP_CONNECTING;
          }
        }
        vTaskDelay(pdMS_TO_TICKS(100));

      } break;
    }
  }
}

void create_net_task(void) {
  xTaskCreate(wifi_task_entry, "wifi_task", 4096, NULL, 5, &net_task_handle);
}

net_state_t get_net_state(void) { return g_state; }
